package com.itcam.designmodule_.singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * Author:Cammy.Wu
 * Date:2024-05-29
 * Description:懒汉式单例
 */

public class LazyMan {

    // 红绿灯就是在任何情况下，可以通过非当前对象来访问当前对象
    private static boolean cammy = false;

    private LazyMan() {

        synchronized (LazyMan.class) {
            if (cammy == false) {
                cammy = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏单例");
            }
        }

        System.out.println(Thread.currentThread().getName() + " ok ");
    }

//    private static LazyMan lazyMan;
    /**
     * volatitle
     * 保证线程的可见性
     * 禁止指令重新排序
     */
    private volatile static LazyMan lazyMan; // volatile adj.易变的，动荡不定的

    // 双重检测锁模式的懒汉式单例 == DCL懒汉式
    public static LazyMan getInstance() {
        // 加锁
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan(); // 它不是一个原子性操作
                    /**
                     * 不是原子性操作会经过的步骤：
                     *      1.分配内存空间
                     *      2.执行构造函数，初始化对象
                     *      3.把这个对象指向这个空间
                     *
                     * TODO：
                     *  我们期望它是123的顺序执行的
                     *  但是它真实可能执行的是132顺序
                     *  如果此时新来一个线程，新来的线程会认为lazyMan!=null
                     *  此时lazyMan还没有完成构造
                     */

                }
            }
        }

        return lazyMan;
    }

    // 单线程下以上代码是ok的

    // 多线程并发以上代码就没用了
//    public static void main(String[] args) {
//        for (int i = 0; i < 10; i++) {
//            new Thread(()->{
//                LazyMan.getInstance();
//            }).start();
//        }
//    }

    // 反射 ==> 破解懒汉式单例
    public static void main(String[] args) throws Exception {
//        LazyMan instance = LazyMan.getInstance();

        // 假设我找到了这个cammy这个变量，我就可以通过这个变量来破坏单例
        Field cammy = LazyMan.class.getDeclaredField("cammy");
        cammy.setAccessible(true);

        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        // 但如果两个对象都是通过declaredConstructor.newInstance()创建的，那么它们还是不同的
        LazyMan instance = declaredConstructor.newInstance();
        cammy.set(instance, false);
        LazyMan instance1 = declaredConstructor.newInstance();


        // 如果被反射破坏，输出结果就不一样
        System.out.println(instance);
        System.out.println(instance1);
    }
}
